// xxx desk is obsolete; now we're using prototype windows

Desk = function() {};
Desk.prototype = {

  /// name of desktop instance by default; trying to be globally unique with respect to any other client div name
  usename: "desk0001",

  /// desktop widgets in this desktop
  elements: {},

  /// div creation parameters
  usezindex: true,
  cascadex: 140,
  cascadey: 20,
  zindex: 2000,
  id: 0,
  minx: 0,
  miny: 0,
  latch_changed: null,

  // utility: concat two things as a file paths
  concat: function(a,b) {
    if(b[0] == '/') b = b.substring(1);
    if(a[a.length-1] == '/') return a+b;
    return a+"/"+b;
  },

  // utility: get url as a path
  path: function() {
    var i = location.href.indexOf('/',7);
	    domain = location.href.substring(7,i).split('.').reverse();
	    path = location.href.substring(i+1).split('/');
	    return "/"+Array.concat(domain,path).join('/');
  },

  ///
  /// creates a div that is intended to be part of the aesthetic layout of another div
  /// for example to build curves along the edge of a box or to do drop shadows
  ///
  parametric_div: function(
                             key,     // part of the name the created piece will have + parent div name
                             parent,  // the parent div if any
                             width,   // width of piece
                             height,  // height of piece
                             top,     // attachment point div absolute positioning if any
                             left,    // attachment point if any
                             bot,     // attachment point if any
                             right,   // attachment point if any
                             cleft,   // color left
                             cright,  // color right
                             ctop,    // color top
                             cbot,    // color bottom
                             cback,   // color back
                             text     // inner html if any
                             )
  {
    key = parent.id + "_" + key;
    var d = document.getElementById(key);
    if(d == null ) {
      // basic stuff
				      d = document.createElement("div");
				      if( parent ) { parent.appendChild(d); }
		   		   d.style.position = "absolute";
				      d.style.overflow = "hidden";
				      d.style.margin = "0";
				      d.style.padding = "0";
				      d.id = key;
      if( text ) d.innerHTML = text;
				      if(!text ) { d.style.fontSize = "0"; }
				      // border colors
				      if( cleft != null ) { d.style.borderLeft = cleft; }
				      if( cright != null ) { d.style.borderRight = "1px solid " + cright; }
				      if( ctop != null ) { d.style.borderTop = "1px solid " + ctop; }
				      if( cbot != null ) { d.style.borderBottom = "1px solid " + cbot; }
				      if( cback != null ) { d.style.backgroundColor = cback; }
				      // positioning relative to parent
				      //bot = null; if( top != null && top < 0 ) { top = null; bot = -top; }
				      //right = null; if( left != null && left < 0 ) { left = null; right = -left; }
				      if( top != null ) { d.style.top = top.toString() + "px"; }
				      if( left != null ) { d.style.left = left.toString() + "px"; }
				      if( bot != null ) { d.style.bottom = bot.toString() + "px"; }
				      if( right != null ) { d.style.right = right.toString() + "px"; }
				    }
				   		 d.style.width = width.toString() + "px";
		    d.style.height = height.toString() + "px";
		    return d;
		  },
				
  ///
  /// rudimentary stretch; could be a toggle with a zoom to full page for the other button xxx
  ///
  desk_minimize: function(id) { document.getElementById(id.toString()+"_h").style.display = "none"; },
  desk_maximize: function(id) { document.getElementById(id.toString()+"_h").style.display = ""; },
  desk_close: function(id) { document.getElementById(id.toString()).style.display = "none"; },

  ///
  /// instance a parametric widgets visual display (consists of multiple divs)
  ///
  parametric_elem: function(key,title,html,left,top,width,height) {

    if( !width || !height) {
      // alert("no width or height for " + html + " " + key + " " + title );
      // actually there can be elements in a folder that are not displayable; so ignore them.
      return null;
    }

    if( !left ) { left = this.cascadex; this.cascadex += 20; if( this.cascadex > 400 ) this.cascadex -= 398; }
    if( !top ) { top = this.cascadey; this.cascadey += 20; if( this.cascadey > 400 ) this.cascadey -= 398; }
    if( top < this.miny ) { top = this.miny; }
    if( left < this.minx ) left = this.minx;

    var fresh = false;
    var div = document.getElementById(key);
    if(!div) {
      fresh = true;
      div = document.createElement("div");
      div.id = key;
      // div backdrop itself
      div.style.position = "absolute";
      div.style.left = left.toString() + "px"
      div.style.top = top.toString() + "px"
      div.style.zIndex = this.zindex++; 
      document.body.appendChild(div);
    }

    // reposition and size
    div.style.left = left.toString() + "px"
    div.style.top = top.toString() + "px"
    div.style.width = width.toString() + "px";
    div.style.height = height.toString() + "px";

    // preferences
    edge = "#6060c0";
    back = "#c0c0ff"
    body = "#e0e0ff"

    // shadows
    // var sa = parametric_div("sa",div,10,   height, 0,width,null,null,null,null,null,null,back,"<img src='vdrop-20x1024.jpg'></img>");
    // var sb = parametric_div("sb",div,width-3,   1, 1,2,null,null,"2px solid "+edge,edge,null,null,back,null);
    // var sc = parametric_div("sc",div,width-1,   2, 2,1,null,null,"1px solid "+edge,edge,null,null,back,null);

    // attempt at shadow
    // var shadow = parametric_div("shadow",div,width,height+24, 4,6,null,null,null,null,null,null,back,null);
    // Element.setOpacity(shadow,0.3);

    // bevel size
    var a = this.parametric_div("a",div,width-4,   1, 0,4,null,null,"1px solid "+edge,edge,edge,null,back,null);
    var b = this.parametric_div("b",div,width-3,   1, 1,2,null,null,"2px solid "+edge,edge,null,null,back,null);
    var c = this.parametric_div("c",div,width-1,   2, 2,1,null,null,"1px solid "+edge,edge,null,null,back,null);
    var d = this.parametric_div("d",div,width,    18, 4,0,null,null,"1px solid "+edge,edge,null,edge,back,"&nbsp;" + title);

    // for nested methods to avoid confusion
    var xfocus = this;

    // enable drag after we set the top drag region ( compliments of prototype.js )
    if(fresh) {
      div.draggable = new Draggable(div,
                       {handle:div.id+"_d",
                        starteffect: function(e) {
                           if(!document.all)Element.setOpacity(div,0.7);
                           if( xfocus.usezindex == false ) { document.body.appendChild(div); }
                           else { div.style.zIndex = this.zindex++; }
                           Draggable._dragging[div] = true;
                           xfocus.latch_changed = div;
                        },
			                        endeffect: function(element) {
			                           if(!document.all)new Effect.Opacity(div,{duration:0.2, from:0.7, to:1.0, queue: {scope:'_draggable', position:'end'}});
			                           Draggable._dragging[div] = false;
                           if( xfocus.usezindex == false ) { document.body.appendChild(div); }
                           else { div.style.zIndex = this.zindex++; }
			                           var x = parseInt(div.style.left);
			                           var y = parseInt(div.style.top);
			                           var w = parseInt(div.style.width);
			                           var h = parseInt(div.style.height);
			                           xfocus.save_location(key,x,y,w,h);
                           xfocus.parametric_elem(div.id,null,null,x,y,w,h);
			                        }
                        });
    }

    // input box controls
    //var e = this.parametric_div("e",div,14,       16, 4,null,null,32,"1px solid "+edge,edge,null,edge,body,"<input style='overflow: hidden; font-family:verdana; font-size:8px; width: 14px; height: 16px;' type='button' value='-' onclick='desk_minimize("+key+");'></input>");
    //var f = this.parametric_div("f",div,14,       16, 4,null,null,16,"1px solid "+edge,edge,null,edge,body,"<input style='overflow: hidden; font-family:verdana; font-size:8px; width: 14px; height: 16px;' type='button' value='O' onclick='desk_maximize("+key+");'></input>");
    var g = this.parametric_div("g",div,14,       16, 4,null,null, 0,"1px solid "+edge,edge,null,edge,body,"<input style='overflow: hidden; font-family:verdana; font-size:8px; width: 14px; height: 16px;' type='button' value='X' onclick='desk_close("+key+");'></input>");
    var h = this.parametric_div("h",div,width,height,23, 0,null,null,"1px solid "+edge,edge,null,edge,body,html);
    var s = this.parametric_div("s",div,14,      16,null,null,-24,-2,"1px solid "+edge,edge,edge,edge,body,null);

    // stretchy sensor
    if(fresh) {
      s.draggable = new Draggable(s,
       {handle:s.id,
        starteffect: function(e) {
           if(!document.all)Element.setOpacity(div,0.7);
           if( this.usezindex == false ) { document.body.appendChild(div); }
           else { div.style.zIndex = this.zindex++; }
           Draggable._dragging[s] = true;
        },
        change: function(e) {
           var x = parseInt(div.style.left);
           var y = parseInt(div.style.top);
           var w = parseInt(s.style.left);
           var h = parseInt(s.style.top);
           if( w < 100 ) w = 100;
           if( h < 32 ) h = 32;
           xfocus.parametric_elem(div.id,null,null,x,y,w+14,h-6);
           s.style.left = w.toString() + "px";
           s.style.top = h.toString() + "px";
        },
        endeffect: function(e) {
          if(!document.all)new Effect.Opacity(div,{duration:0.2, from:0.7, to:1.0, queue: {scope:'_draggable', position:'end'}});
          Draggable._dragging[s] = false;
          if( this.usezindex == false ) { document.body.appendChild(div); }
          else { div.style.zIndex = this.zindex++; }
          var x = parseInt(div.style.left);
			          var y = parseInt(div.style.top);
			          var w = parseInt(div.style.width);
			          var h = parseInt(div.style.height);
			          xfocus.save_location(key,x,y,w,h);
        }
     });
    }
    return div;
  },

  // ************************************************************************
  // ************************************************************************
  // ************************************************************************

  save_location: function(key,x,y,width,height) {
    var element = this.elements[key];
    if(!element) return false;
    params = "uid="+element.uid+"&x="+x+"&y="+y+"&width="+width+"&height="+height;
    new Ajax.Request("/edit",
     { method: 'post',
       asynchronous: true,
       evalScripts: true,
       parameters: params
     });
    return false;
  },

  ///
  /// find
  ///
  find_canonical_element: function(element) {
    element.key = this.usename + "_" + element.id;
    if(!element || !element.key) { 
      alert("no id " + element.description ); return;
    }
    return this.elements[element.key];
  },

  ///
  /// server side ajax callback 
  ///
  callback_handler: function(text) {
    if(!text || text.length < 10) return;
    var results;
    try {
      results = eval('('+text+')');
    } catch(err) {
      alert('failed to handle ' + text );
      return false;
    }
    if(!results) return false;
    if(results.constructor != Array) {
      results = [results];
    }
    for(var i = results.length - 1; i >= 0; i-- ) {
      var element = results[i].attributes;
      //an attempt to stop server side interfering with drag and drop
      //var temp = this.find_canonical_element(element);
      //if(this.latch_changed && temp.div == latch_changed) {
      //continue;
      //}
      element.key = this.usename + "_" + element.id;
      if(!element || !element.key) { 
        alert("no id " + element.description ); return;
      }
      // xxx currently we keep the existing element and throw away most of new
      //     it may be better to reverse this policy; debate
      //     there are some subtle state issues like 'on top' and 'being dragged'
      //     that would have to be preserved if we reversed the order
      var found = this.elements[element.key];
      if( found ) {
        found.extended = element.extended;
        element = found;
      } else {
        this.elements[element.key] = element;
      }
      this.remake_one(element)
    }
    if(!element)alert('bad data ' + text);
    return false;
  },

  ///
  /// re layout
  ///
  remake_one: function(element,e) {

    var xfocus = this;

    // get position from extended area
    element.extended.split('&').each( function(term) {
       parts = term.split('=');
       element[parts[0]] = parts[1];
     }
    );

    // if already present apply layout (move it)
    if(element.div) {
         element.div = xfocus.parametric_elem(
                            element.key,
                            element.title,
                            element.text,
                            element.x,
                            element.y,
                            element.width,
                            element.height
                            );
      return false;
    }

    // do not re-fetch content if already fetched once
    if(element.fetched) {
      return false;
    }
    element.fetched = true;

    // must load it for first time; i set a random position for new ones
    // xxx should respect the size supplied
    // xxx passing the handler is just for fun - can remove ideally; server should know already
    new Ajax.Request("/contents",
     { method: 'post',
       asynchronous: true,
       evalScripts: true,
       parameters: "handler="+element.handler+"&uid="+element.uid,
       onComplete: function(r) {

         // embed html and finalize layout
         element.text = r.responseText;

         element.div = xfocus.parametric_elem(
                            element.key,
                            element.title,
                            element.text,
                            element.x,
                            element.y,
                            element.width,
                            element.height
                            );

         // re-register if not registered bug (xxx weird)
         xfocus.elements[element.id] = element;
         xfocus.elements[element.id].div = element.div;

         // peel out first javascript command and run it
         if ( r.responseText ) {
           var a = r.responseText.indexOf("<script"); a = r.responseText.indexOf(">",a) + 1;
           var b = r.responseText.indexOf("</script>");
           if(a > 0 && b > a) {
             var c = r.responseText.substring(a,b);
             eval(c);
           }
         }
       }
     });
    return false;
  },

  // build just about anything on demand
  create: function(uid,params,rules) {
    var xfocus = this;

    // if no uid or non-base set uid is supplied then qualify or at least supply relative path
    if(!uid || uid[0] != '/' ) {
      var path = this.path();
      if(uid) {
        uid = this.concat(path,uid);
      } else {
        uid = this.concat(path,"/");
      }
    }
    params = "uid="+uid+"&json=1&"+params
	
    // widgets accept a position that we can store in extended part of note
    if( params.indexOf("&x=") < 1 && params.indexOf("&width=" > 0)) {
      left = 0;
      top = 0;
      if( !left ) { left = this.cascadex; this.cascadex += 20; if( this.cascadex > 400 ) this.cascadex -= 398; }
      if( !top ) { top = this.cascadey; this.cascadey += 20; if( this.cascadey > 400 ) this.cascadey -= 398; }
      params += "&x="+left+"&y="+top
    }

    // submit
    new Ajax.Request('/edit',
     { method: 'post',
       asynchronous: true,
       evalScripts: true,
       parameters: params,
       onComplete: function(request) {
         var e = xfocus.callback_handler(request.responseText);
       }
     });
    return false;
  },

  findYPosition: function(obj) {
    // http://www.quirksmode.org/js/findpos.html
	    var curleft = curtop = 0;
	    if (obj.offsetParent) {
		      curleft = obj.offsetLeft
		      curtop = obj.offsetTop
		      while (obj = obj.offsetParent) {
			        curleft += obj.offsetLeft
			        curtop += obj.offsetTop
		      }
    	}
    	return curtop;
  },

  startup: function() {
    var xfocus = this;
    var path = this.path();

    /*
    // timed refetch state of desktop (remote users may be manipulating it)
    new PeriodicalExecuter(function() {
      new Ajax.Request("/query",
       { method: 'post',
         asynchronous: true,
         evalScripts:true,
         parameters: "uid="+path+"&order=desc&limit=100",
         onComplete:function(r) { xfocus.callback_handler(r.responseText) }
       })
      },4);
    */

    // show all once now
    new Ajax.Request("/query",
     { method: 'post',
       asynchronous: true,
       evalScripts: true,
       parameters: "uid="+path+"&order=desc&limit=100",
       onComplete: function(r) { xfocus.callback_handler(r.responseText); }
     });

    /*
    // example: how to load hardcoded content so you would not need any server side
    new Ajax.Request("/desk/content.js",
     { method: 'post',
       asynchronous: true,
       evalScripts: true,
       parameters: "uid="+path+"&order=desc&limit=100",
       onComplete: function(r) { xfocus.callback_handler(r.responseText); }
     });
     */

    return false;
  }
}

